// validation_pipe.ts
import {
  Injectable,
  PipeTransform,
  ArgumentMetadata,
  ValidationError,
  UnauthorizedException,
} from '@nestjs/common';
import { plainToClass } from 'class-transformer';
import { validate } from 'class-validator';

/**
 * 这是一个全局的参数验证管道，基于class-transformer
 * 如果失败，则会抛出Error
 * 使用 dto 的时候只会使用在 dto 中定义的参数，并且会做参数校检，可以直接使用 dto 的值进行传入
 */

@Injectable()
export class ValidationPipe implements PipeTransform {
  async transform(
    paramValue: any,
    { metatype: paramMetatype }: ArgumentMetadata,
  ) {
    if (!paramMetatype || !this.toValidate(paramMetatype)) {
      return paramValue;
    }
    const object = plainToClass(paramMetatype, paramValue);

    const errors = await validate(object, {
      whitelist: true, // 白名单选项
      forbidNonWhitelisted: true, // 禁用非白名单属性选项
      stopAtFirstError: true, // 碰到第一个错误就返回
      forbidUnknownValues: true, // 禁用未知的值
    });
    const errorList: string[] = [];
    const errObjList: ValidationError[] = [...errors];

    do {
      const e = errObjList.shift();
      if (!e) {
        break;
      }
      if (e.constraints) {
        for (const item in e.constraints) {
          if (item === 'whitelistValidation') {
            // 如果是白名单校验错误的，使用自定义的错误语句描述。
            errorList.push(`属性 ${e.property} 未定义!`);
          } else {
            errorList.push(e.constraints[item]);
          }
        }
      }
      if (e.children) {
        errObjList.push(...e.children);
      }
    } while (true);

    if (errorList.length > 0) {
      //   throw new Error(`Error:请求参数校验错误: ${errorList.join()}`);
      throw new UnauthorizedException(
        `Error:请求参数校验错误: ${errorList.join()}`,
      );
    }
    return object;
  }

  private toValidate(paramMetatype: any): boolean {
    const types: any = [String, Boolean, Number, Array, Object];
    return !types.includes(paramMetatype);
  }
}
